package com.bishe.app.modules.user.controller;

import cn.hutool.db.PageResult;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.bishe.app.core.api.CommonResult;
import com.bishe.app.core.api.CommonPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bishe.app.core.enums.IsDel;
import com.bishe.app.core.utils.AppRunnerUtils;
import com.bishe.app.core.utils.Lock;
import com.bishe.app.modules.group.entity.Group;
import com.bishe.app.modules.group.model.GroupNum;
import com.bishe.app.modules.group.service.GroupService;
import com.bishe.app.modules.groupuser.entity.GroupUser;
import com.bishe.app.modules.groupuser.enums.GroupUserPosition;
import com.bishe.app.modules.groupuser.service.GroupUserService;
import com.bishe.app.modules.menu.entity.Menu;
import com.bishe.app.modules.menu.entity.MenuTree;
import com.bishe.app.modules.menu.service.MenuService;
import com.bishe.app.modules.role.entity.Role;
import com.bishe.app.modules.role.service.RoleService;
import com.bishe.app.modules.rolemenu.entity.RoleMenu;
import com.bishe.app.modules.rolemenu.service.RoleMenuService;
import com.bishe.app.modules.user.model.*;
import com.bishe.app.modules.userrole.entity.UserRole;
import com.bishe.app.modules.userrole.service.UserRoleService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import com.bishe.app.modules.user.service.UserService;
import com.bishe.app.modules.user.entity.User;

import java.util.*;
import java.util.stream.Collectors;

import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;


/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author
 * @since 2024-03-13
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    public UserService userService;

    @Resource
    private UserRoleService userRoleService;

    @Resource
    private GroupUserService groupUserService;

    @Resource
    private GroupService groupService;

    @Resource
    private RoleService roleService;

    @Resource
    private RoleMenuService roleMenuService;

    @Resource
    private MenuService menuService;

    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @ResponseBody
    @Transactional(rollbackFor = Throwable.class)
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult create(@RequestBody UserAddCmd user) {
        // 校验名字不能相同
        List<User> list = userService.lambdaQuery().eq(User::getName, user.getName())
                .list();

        if (!CollectionUtils.isEmpty(list)) {
            return CommonResult.failed("已经存在相同名字用户。请使用其他用户名.");
        }

        // 校验只能有一个店长。
        if (Objects.equals(user.getLeaderId(), GroupUserPosition.LEADER.getCode()) ||
                Objects.equals(user.getLeaderId(), GroupUserPosition.XIEDIAN_LEADER.getCode())
        ) {
            List<GroupUser> list1 = groupUserService.lambdaQuery()
                    .eq(GroupUser::getGroupId, user.getGroupId())
                    .eq(GroupUser::getIsDel, IsDel.N.getCode())
                    .in(GroupUser::getLeader,
                            Arrays.asList(
                                    GroupUserPosition.LEADER.getCode(),
                                    GroupUserPosition.XIEDIAN_LEADER.getCode()
                            ))
                    .list();

            if (!CollectionUtils.isEmpty(list1)) {
                return CommonResult.failed("店铺已经存在店长。不能重复添加.");
            }
        }

        boolean success = userService.save(user);
        if (user.getGroupId() != null) {
            GroupUser gu = new GroupUser();
            gu.setUserId(user.getId());
            gu.setGroupId(user.getGroupId());
            gu.setLeader(user.getLeaderId());
            groupUserService.save(gu);
        }
        if (success) {
            return CommonResult.success(null);
        }
        return CommonResult.failed();
    }

    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
    @ResponseBody
    @Transactional(rollbackFor = Throwable.class)
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult update(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        boolean success = userService.updateById(user);
        if (success) {
            return CommonResult.success(null);
        }
        return CommonResult.failed();
    }

    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
    @ResponseBody
    @Transactional(rollbackFor = Throwable.class)
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult delete(@PathVariable Long id) {
        boolean success = userService.removeById(id);

        groupUserService.lambdaUpdate()
                .set(GroupUser::getIsDel,IsDel.Y.getCode())
                .eq(GroupUser::getUserId,id)
                .update();
        if (success) {
            return CommonResult.success(null);
        }
        return CommonResult.failed();
    }

    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    @ResponseBody
    @Transactional(rollbackFor = Throwable.class)
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult deleteBatch(@RequestParam("ids") List<Long> ids) {
        boolean success = userService.removeByIds(ids);
        if (success) {
            return CommonResult.success(null);
        }
        return CommonResult.failed();
    }


    @RequestMapping(value = "/listAll", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<User>> listAll() {
        List<User> userList = userService.list();
        return CommonResult.success(userList);
    }

    @RequestMapping(value = "/pageList", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<CommonPage<User>> page(@RequestBody CommonPage commonPage) {
        Page<User> page = new Page(commonPage.getPageNum(), commonPage.getPageSize());
        return CommonResult.success(CommonPage.restPage(userService.page(page)));
    }

    @RequestMapping(value = "/userInfo", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<UserDTO> getUserInfo() {
        UserDTO userInfo = userService.getUserInfo(AppRunnerUtils.getUser());
        return CommonResult.success(userInfo);
    }


    @RequestMapping(value = "/getUserPwd", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<User> getUserInfoPwd(@RequestParam("id") Long userId) {
        User byId = userService.getById(userId);
        return CommonResult.success(byId);
    }

    @RequestMapping(value = "/resetSpecialUserPwd", method = RequestMethod.POST)
    @ResponseBody
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult resetSpecialUserPwd(@RequestBody User user) {
        userService.lambdaUpdate()
                .eq(User::getId, user.getId())
                .set(User::getName, user.getName())
                .set(User::getPwd, user.getPwd())
                .update();

        return CommonResult.success(null);
    }


    @RequestMapping(value = "/resetPwd", method = RequestMethod.POST)
    @ResponseBody
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult resetCurPwd(@RequestBody UserResetPwdDTO resetPwdDTO) {
        if (resetPwdDTO == null) {
            return CommonResult.failed("重置请求不能为空");
        }
        User user = AppRunnerUtils.getUser();
        User oldUser = userService.getById(user.getId());

        if (!oldUser.getPwd().equals(resetPwdDTO.getOldPass())) {
            return CommonResult.failed("原密码不对");
        }

        if (!resetPwdDTO.getCheckPass().equals(resetPwdDTO.getNewPass())) {
            return CommonResult.failed("确认密码和新密码不匹配");
        }

        // 开始重置
        userService.lambdaUpdate()
                .set(User::getPwd, resetPwdDTO.getNewPass())
                .eq(User::getId, oldUser.getId())
                .update();
        return CommonResult.success(null);
    }

    @RequestMapping(value = "/searchKw", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<List<User>> page(@RequestBody UserKwSearch userSearch) {
        if (userSearch.getUsername() == null) {
            return CommonResult.success(new ArrayList<>());
        }

        List<User> userList = userService.lambdaQuery()
                .like(User::getName, userSearch.getUsername())
                .eq(User::getIsDel, IsDel.N.getCode())
                .list();

        return CommonResult.success(userList);
    }


    @RequestMapping(value = "/search", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<CommonPage<? extends User>> page(@RequestBody UserSearch userSearch) {

        List<Long> userIdList = new ArrayList<>();
        if (userSearch.getGroupType() != null || userSearch.getGroupId() != null) {
            List<Group> list = groupService.lambdaQuery()
                    .eq(Objects.nonNull(userSearch.getGroupId()), Group::getId, userSearch.getGroupId())
                    .eq(Objects.nonNull(userSearch.getGroupType()), Group::getType, userSearch.getGroupType())
                    .eq(Group::getIsDel, IsDel.N.getCode())
                    .list();

            if (CollectionUtils.isEmpty(list)) {
                return CommonResult.success(CommonPage.restPage(new Page<>(userSearch.getPageNum(), userSearch.getPageSize(), 0)));
            }


            List<Long> groupIdList = list.stream().map(g -> g.getId())
                    .collect(Collectors.toList());

            List<GroupUser> guList = groupUserService.lambdaQuery()
                    .in(GroupUser::getGroupId, groupIdList)
                    .eq(Objects.nonNull(userSearch.getId()), GroupUser::getUserId, userSearch.getId())
                    .list();

            if (CollectionUtils.isEmpty(guList)) {
                return CommonResult.success(CommonPage.restPage(new Page<>(userSearch.getPageNum(), userSearch.getPageSize(), 0)));
            }

            Set<Long> userSet = guList.stream().map(x -> x.getUserId())
                    .collect(Collectors.toSet());

            userIdList.addAll(userSet);
        }


        Page<User> page = new Page(userSearch.getPageNum(), userSearch.getPageSize());

        LambdaQueryChainWrapper<User> eq = userService.lambdaQuery();
        eq.in(!CollectionUtils.isEmpty(userIdList), User::getId, userIdList);

        if (Objects.nonNull(userSearch.getName())) {
            eq.like(User::getName, userSearch.getName());
        }
        Page<? extends User> userResult = eq.page(page);
        if (!CollectionUtils.isEmpty(userResult.getRecords())) {
            List dtoList = userResult.getRecords().stream()
                    .map(record -> {
                        UserInfo userDTO = new UserInfo();
                        BeanUtils.copyProperties(record, userDTO);

                        List<GroupUser> groupUserList = groupUserService.lambdaQuery()
                                .eq(GroupUser::getUserId, record.getId())
                                .eq(GroupUser::getIsDel, IsDel.N.getCode())
                                .list();

                        if (!CollectionUtils.isEmpty(groupUserList)) {
                            List<Long> groupIds = groupUserList.stream().map(GroupUser::getGroupId)
                                    .collect(Collectors.toList());
                            List<Group> groupList = groupService.lambdaQuery()
                                    .in(Group::getId, groupIds)
                                    .list();

                            List<GroupNum> groupNums = new ArrayList<>();
                            for (Group group : groupList) {
                                GroupNum groupNum = new GroupNum();
                                BeanUtils.copyProperties(group, groupNum);
                                Optional<GroupUser> guPresent = groupUserList.stream()
                                        .filter(x -> {
                                            return x.getGroupId().equals(group.getId()) &&
                                                    x.getUserId().equals(userDTO.getId());
                                        })
                                        .findFirst();

                                if (guPresent.isPresent()) {
                                    groupNum.setLeaderId(guPresent.get().getLeader());
                                    groupNum.setPositionName(GroupUserPosition.ofCode(groupNum.getLeaderId()).getDesc());
                                }

                                groupNums.add(groupNum);
                            }

                            userDTO.setGroupList(groupNums);
                        }

                        return userDTO;
                    }).collect(Collectors.toList());
            userResult.setRecords(dtoList);
        }
        return CommonResult.success(CommonPage.restPage(userResult));
    }


    @RequestMapping(value = "/getUserRoleList/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<Role>> getUserRoleList(@PathVariable Long userId) {
        if (Objects.isNull(userId)) {
            return CommonResult.success(new ArrayList<>());
        }

        List<UserRole> userRoles = userRoleService.lambdaQuery()
                .eq(UserRole::getUserId, userId)
                .eq(UserRole::getIsDel, IsDel.N.getCode())
                .list();

        if (CollectionUtils.isEmpty(userRoles)) {
            return CommonResult.success(new ArrayList<>());
        }

        List<Long> roleIds = userRoles.stream()
                .map(UserRole::getRoleId)
                .collect(Collectors.toList());

        List<Role> roles = roleService.lambdaQuery()
                .in(Role::getId, roleIds).list();


        return CommonResult.success(roles);
    }


    @RequestMapping(value = "/getUserPermissionTree/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<MenuTree>> getUserPermission(@PathVariable Long userId) {
        CommonResult<List<Role>> userRoleList = getUserRoleList(userId);
        List<Role> roles = userRoleList.getData();
        if (CollectionUtils.isEmpty(roles)) {
            return CommonResult.success(new ArrayList<>());
        }


        List<Long> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList());
        List<RoleMenu> roleMenuList = roleMenuService
                .lambdaQuery()
                .in(RoleMenu::getRoleId, roleIds)
                .eq(RoleMenu::getIsDel, IsDel.N.getCode())
                .list();

        if (CollectionUtils.isEmpty(roleMenuList)) {
            return CommonResult.success(new ArrayList<>());
        }


        Set<Long> menuIds = roleMenuList.stream()
                .map(RoleMenu::getMenuId)
                .collect(Collectors.toSet());

        List<Menu> menuList = menuService.lambdaQuery()
                .in(Menu::getId, menuIds)
                .eq(Menu::getIsDel, IsDel.N.getCode())
                .list();

        if (CollectionUtils.isEmpty(menuList)) {
            return CommonResult.success(new ArrayList<>());
        }

        return CommonResult.success(MenuTree.getTree(menuList));
    }


    @RequestMapping(value = "/addRoles", method = RequestMethod.POST)
    @ResponseBody
    @Transactional(rollbackFor = Throwable.class)
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult<String> addUserRoleList(@RequestBody UserRolesAddCmd userRolesAddCmd) {
        List<UserRole> userRoles = new ArrayList<>();
        for (Long roleId : userRolesAddCmd.getRoleIds()) {
            UserRole userRole = new UserRole();
            userRole.setRoleId(roleId);
            userRole.setUserId(userRolesAddCmd.getUserId());
            userRoles.add(userRole);
        }
        userRoleService.saveBatch(userRoles);
        return CommonResult.success("OK");
    }

    @RequestMapping(value = "/listAllNotDel/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<Role>> listAllNotDel(@PathVariable Long userId) {
        List<Role> roleList = roleService
                .lambdaQuery()
                .eq(Role::getIsDel, IsDel.N.getCode())
                .list();


        CommonResult<List<Role>> userRoleList = getUserRoleList(userId);
        if (!CollectionUtils.isEmpty(userRoleList.getData())) {
            Set<Long> userHasRoleIds = userRoleList.getData()
                    .stream()
                    .map(x -> x.getId())
                    .collect(Collectors.toSet());


            roleList = roleList.stream()
                    .filter(x -> !userHasRoleIds.contains(x.getId()))
                    .collect(Collectors.toList());
        }

        return CommonResult.success(roleList);
    }


    @RequestMapping(value = "/getUserGroupPosition", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<GroupUser> listAllNotDel(@RequestBody GroupUser groupUser) {
        if (groupUser == null || groupUser.getGroupId() == null) {
            return CommonResult.success(null);
        }

        GroupUser one = groupUserService.lambdaQuery()
                .eq(GroupUser::getUserId, groupUser.getUserId())
                .eq(GroupUser::getGroupId, groupUser.getGroupId())
                .eq(GroupUser::getIsDel,IsDel.N.getCode())
                .one();

        return CommonResult.success(one);
    }

    @RequestMapping(value = "/enableUserClick", method = RequestMethod.GET)
    @ResponseBody
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult<GroupUser> enableUserClick(Long userId) {
        userService.lambdaUpdate()
                .eq(User::getId,userId)
                .set(User::getIsDel,IsDel.N.getCode())
                .update();
        return CommonResult.success(null);
    }

    @RequestMapping(value = "/disableUserClick", method = RequestMethod.GET)
    @ResponseBody
    @Lock(lockPrefix = "/user",key = "lock", lockSecond = 60)
    public CommonResult<GroupUser> disableUserClick(Long userId) {
        userService.lambdaUpdate()
                .eq(User::getId,userId)
                .set(User::getIsDel,IsDel.Y.getCode())
                .update();
        return CommonResult.success(null);
    }

}






